#ifndef ANDROID_SERVICE_HH
#define ANDROID_SERVICE_HH

/*
 * @file    harris_node.h
 *
 * Created on: Mar 10, 2012
 * Authors: Zeddy Chirombe, Alex Drake 
 *          {i.z.chirombe, alex.drake}@wustl.edu
 *
 * This file carries our Linux/Android Bridge implementation. On the Linux
 * side, it relys on ROS message service as the communication medium to 
 * interact with all other nodes that wish to engage in voice aided 
 * Robot/Human iteraction. These nodes interested in Android services, must
 * subscribe to android topic, and must advertize their unique inbox where
 * response will be send out. 
 *
 */



#include <iostream>
#include <string>
#include <sstream>
#include <iterator>
#include <map>

#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <unistd.h>

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <android_service/storage_msgAction.h>
#include <android_service/android.h>
#include <actionlib/client/simple_action_client.h>
#include <iostream>

#include "android_service/android_message.h"

using namespace std;

extern int errno;
const int MAX_MSG_SIZE = 256;
const float ROS_SCAN_RATE = 1.5;

enum NetStatusCodes {
    SUCCESS, /* keep going */
    TIMEOUT, /* Timeout, decide whether to keep waiting for abandon request*/
    SOCKET_CLOSED, /* remote android server as died, change state to connect mode */
    MALFORMED_MSG /* Message Rejected, */
};

typedef actionlib::SimpleActionClient<android_service::storage_msgAction> Client;

class Android_Service
{
public:
    Android_Service(Client *client, char* hostname, int port);
    ~Android_Service();

    int open();
    int send_msg(Message *hMsg);
    int recv_msg(Message* hMsg, int timeout);
    void androidBridge(Message& msg, int ttl);
    void showBiometrics(Message& msg);
    void voiceBox();
    void usage();
    string itos(int i);

	
    void android_callback(const std_msgs::String::ConstPtr& msg);
    void storage_callback(const android_service::android& msg);
    
    static int message_counter;

private:
    // android stuff - 
    bool chanState;
    string hostname;
    int sockfd, port, n;
    struct sockaddr_in serv_addr;
    struct hostent *server;
    char buffer[MAX_MSG_SIZE];
    
    ros::NodeHandle nh_;

    ros::Publisher android_pub;
    ros::Subscriber android_sub;
    ros::Subscriber storage_sub;

    Client *client;


};
#endif


int Android_Service::message_counter = 1000;

Android_Service::Android_Service(Client* _client, char* hostname, int port ) 
 : client(_client), hostname(hostname), port(port)
{
    
    android_pub = nh_.advertise<std_msgs::String>("android_response", 1);
    android_sub = nh_.subscribe("android_test",1, &Android_Service::android_callback, this);
    storage_sub = nh_.subscribe("android_request",1, &Android_Service::storage_callback, this);

}
Android_Service::~Android_Service()
{
    
}

int Android_Service::open() {
    chanState = false;
    port = port;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) { 
        cout <<"ERROR opening socket"<<endl;
        return -1;
    }
    server = gethostbyname(hostname.c_str());
    if (server == NULL) {
        cout <<"ERROR, no such host"<<endl;;
        return -1;
    }
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
    serv_addr.sin_port = htons(port);
    if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) {
        cout <<"ERROR connecting"<<endl;
        return -1;
    }

    // ---Not sure why adb is not picking messages from HTC on time - 
    unsigned int opt = 4, one = 1;
    setsockopt(sockfd, IPPROTO_TCP,TCP_NODELAY,(void *)&opt, sizeof(opt));
    setsockopt(sockfd, IPPROTO_TCP,SO_KEEPALIVE,(void *)&opt, sizeof(opt));
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void*)&one, sizeof(one));
 
    Message hMsg;
    int ret = recv_msg(&hMsg, 4);
    if((ret == TIMEOUT) || (ret == SOCKET_CLOSED)){
        cout <<" --- No Android Server - exiting....."<<endl;
        return -1;
    }
    cout <<"[ "<<hMsg.str()<<" ] ...."<<endl;
    chanState = true;


    // - start reading thread - 
    //pthread_create(&request_deport_t,NULL,&Android_Service::_android_read_,(void*)this);
    //pthread_detach(request_deport_t);
    //cout <<"Done Setting up androidMsg Reader ......"<<endl; 

    return 0;
}
    
int Android_Service::send_msg(Message *hMsg) {
    if((hMsg->message_type() <= 0) || (hMsg->message_type() > ANDROID_USER_LIMIT)) {
        return MALFORMED_MSG;
    } 
    hMsg->add_entry("\n");
    int msg_len = hMsg->android_str().length();
    int n = write(sockfd,hMsg->android_str().c_str(),msg_len);
    if(n <= 0) { 
        chanState = false;
        return SOCKET_CLOSED;
    }
    return SUCCESS;
}
int Android_Service::recv_msg(Message* hMsg, int timeout) {
    
    int ret;

    fd_set data;
    struct timeval tv;
    tv.tv_sec  = timeout;
    tv.tv_usec = 0;
    FD_ZERO(&data);
    FD_SET(sockfd, &data);
        errno = 0;
    ret = select(FD_SETSIZE, &data,NULL,NULL,&tv);
    if(ret < 0) {
        cout <<"Error:recv_msg("<<errno<<"): select() - Failed"<<endl;
        return SOCKET_CLOSED;
    } else if(ret == 0) {
        return TIMEOUT;
    } else if(FD_ISSET(sockfd,&data)) {
        memset(buffer,0,MAX_MSG_SIZE);
        errno = 0;
        ret = recv(sockfd,buffer,MAX_MSG_SIZE,0);
        if(ret <= 0) {
            cout <<"Error: recv_msg("<<errno<<") - Network partner not available..."<<endl;
            chanState = false;
            return SOCKET_CLOSED;
        }
        buffer[ret-1] = '\0';
        hMsg->read(buffer, true);
        //cout <<"   raw-msg[ "<<buffer<<" ] "<<endl;
        //cout <<"android-msg[ "<<hMsg->str()<<" ] "<<endl;
    }
    return SUCCESS;
}

void Android_Service::androidBridge(Message& msg, int ttl) {
        
    cout <<"----androidBridge("<<msg.size()<<")------------"<<endl;
    if(msg.message_type() != ANDROID_UNDEFINED) {
        // push user message to android 
        cout <<"Sending MSG[ "<<msg.android_str()<<" ]...."<<endl;
        int ret = send_msg(&msg);
        if(ret != SUCCESS) {
            cout <<"Error: send_msg() : Failed to send message....exiting..."<<endl;
            return;
        }
        // wait for response for all non-broadcast messages - 
        if(msg.message_type() == ANDROID_BROADCAST) {
            sleep(3); 
        } else 
        if(!(msg.message_type() == ANDROID_BROADCAST)) {
            Message msgIn;
            // read response from Android - 
            ret = recv_msg(&msgIn, ttl);
            if(ret == TIMEOUT) { 
                cout <<"info: no messages to read.....sleeping some more- "<<endl;
                return;
            }
            if(ret == SOCKET_CLOSED) {
                cout <<"Error: send_msg() : Failed to send message....exiting..."<<endl;
                chanState = 0;
                exit(0);
            }
            //if(msgIn.message_type() != ANDROID_UNDEFINED) {
            if(msgIn.message_type() == ANDROID_BIOMETRICS) {
                showBiometrics(msgIn);
            } else {
                cout <<"\nVoiceBox:: [ "<<msgIn.str()<<" ]"<<endl;
            }
        }
        msg.reset();
    }
}


void Android_Service::showBiometrics(Message& msg) {
    int i = 0;
    stringstream strbuf;
    if(msg.size() < 7) {
        cout <<"error: Malformed biometric msg[ "<<msg.str()<<" ] "<<endl;
        return;
    }
    strbuf <<"Facial Biometric Signature:"<<endl;
    strbuf <<"         MsgType[ ANDROID_BIOMETRICS "<<endl;
    strbuf <<"        UserName[ "<<msg.entry(i++)<<endl;
    strbuf <<"           MsgId[ "<<msg.entry(i++)<<endl;
    strbuf <<"Confidence Level[ "<<msg.entry(i++)<<endl;
    strbuf <<"    EyesDistance[ "<<msg.entry(i++)<<endl;
    strbuf <<"    eyesMidPts.x[ "<<msg.entry(i++)<<endl;
    strbuf <<"    eyesMidPts.y[ "<<msg.entry(i++)<<endl;
    strbuf <<"        imageUrl[ "<<msg.entry(i++)<<endl;
    cout <<strbuf.str()<<endl;
}

void Android_Service::voiceBox() {
    stringstream strbuf;
    string myBuffer;

    while(chanState) {
        Message androidMsg;
        androidMsg.reset();

        cout <<"Enter Activity : \n  --> ";
        memset(buffer,0,MAX_MSG_SIZE);
        if(cin.getline(buffer,MAX_MSG_SIZE).eof() || (strlen(buffer) < 5)) {
            break;
        }   
        myBuffer.assign(buffer);

        if(strncmp(myBuffer.c_str(),"-h",2) == 0) {
            usage();
            continue;
        }

        if(androidMsg.read(myBuffer, false) != 0) {
            cout <<"Error: [ "<<buffer<<" ] AndroidMsg malformed..."<<endl;
            continue;
        }

        if(androidMsg.message_type() == ANDROID_UNDEFINED) {
            cout <<"Undefined messagetype "<<endl;
            continue;
        }
        androidMsg.insert_entry(1,itos(message_counter++));
        androidBridge(androidMsg, 25);
        //sleep(3);
    }
}

void Android_Service::usage() {
    string usage = "usag()::\n   VoiceClient  -h\n";
    usage += "   VoiceClient <msgType>:<clientid>:<field1>:<field2>...<fieldn>\n";
    usage += "      where msgTye {\n";
    usage += "\t[3] ANDROID_BROADCAST,  // one way voice message - \n";
    usage += "\t[4] ANDROID_QUESTION,   // Question with replay - \n";
    usage += "\t[5] ANDROID_GRID_LOCATION   // Ask humam for a desired map location - \n";
    usage += "\t[6] ANDROID_BIOMETRICS // ask Android to generate facial biometric signature - \n";
    usage += "      }\n\n";
    usage += "      e.g. 3:zeddy:Ask humam for a desired map location\n";
    usage += "      e.g. 4:zeddy:Could you help me please?\n";
    usage += "      e.g. 5:zeddy:Where is west point? Touch it on my touch Tablet.\n";
    usage += "      e.g. 6:zeddy:/sdcard/data/zeddy.png\n";
    usage += "----------------------------------------------------------"; 
    cout <<usage<<endl;
}
string Android_Service::itos(int i) {
    stringstream ss;
    ss << i;
    return ss.str();
}



// ros start here - 




void Android_Service::android_callback(const std_msgs::String::ConstPtr& msg) 
{
    cout <<"Received Msg [ "<<msg->data<<" ] "<<endl;
    stringstream ss;
    ss << "Your Reply [ "<<msg->data<<" ] "<<endl;
    std_msgs::String rmsg;
    rmsg.data = ss.str();
    android_pub.publish(rmsg);
}

void Android_Service::storage_callback(const android_service::android& msg)
{
    cout <<"Received Msg [ "<<msg.record[0]<<" ] "<<endl;
    client->waitForServer();
    android_service::storage_msgGoal goal;
    goal.record.push_back("hi!");
    client->sendGoal(goal);
    client->waitForResult();
/*
    stringstream ss;
    std_msgs::String _msg;
    ss <<"Android Says[ "<<msg->data.c_str()<<" ] ";
    _msg.data = ss.str(); ss.str("");
    android_pub.publish(msg);
**/ 
}


int main(int argc, char** argv)
{
    //srand ( time(NULL) );
    ros::init(argc, argv, "Android_Service");
	
    Client client("storage_msg",true);
    Android_Service asv(&client, "localhost", 7500);
    asv.usage();
    /*    open to activate android comms ***
    if(asv.open() != 0) {
        cout <<"Error: Can't connect to the server....exiting..."<<endl<<endl;
        return -1;
    }
    asv.voiceBox();
    */ 
    ros::spin();
    return 0;
}
